#ifdef __cplusplus
 extern "C" {
#endif
#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdarg.h>
#include <syslog.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/sysinfo.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>

/* ltoh: little to host */
/* htol: little to host */
#if __BYTE_ORDER == __LITTLE_ENDIAN
#  define ltohl(x)       (x)
#  define ltohs(x)       (x)
#  define htoll(x)       (x)
#  define htols(x)       (x)
#elif __BYTE_ORDER == __BIG_ENDIAN
#  define ltohl(x)     __bswap_32(x)
#  define ltohs(x)     __bswap_16(x)
#  define htoll(x)     __bswap_32(x)
#  define htols(x)     __bswap_16(x)
#endif

#define FPGA_DDR_START_ADDR (0)


static unsigned char *h2c_align_mem_tmp;
static unsigned char *c2h_align_mem_tmp;

int c2h_dma_fd;
int h2c_dma_fd;


static void timespec_sub(struct timespec *t1, const struct timespec *t2)
{
  assert(t1->tv_nsec >= 0);
  assert(t1->tv_nsec < 1000000000);
  assert(t2->tv_nsec >= 0);
  assert(t2->tv_nsec < 1000000000);
  t1->tv_sec -= t2->tv_sec;
  t1->tv_nsec -= t2->tv_nsec;
  if (t1->tv_nsec >= 1000000000)
  {
    t1->tv_sec++;
    t1->tv_nsec -= 1000000000;
  }
  else if (t1->tv_nsec < 0)
  {
    t1->tv_sec--;
    t1->tv_nsec += 1000000000;
  }
}


int pcie_init()
{
    c2h_dma_fd = open("/dev/xdma0_c2h_0",O_RDWR | O_NONBLOCK);
    if(c2h_dma_fd < 0)
        return -1;
    h2c_dma_fd = open("/dev/xdma0_h2c_0",O_RDWR );
    if(h2c_dma_fd < 0)
        return -2;

    posix_memalign((void *)&h2c_align_mem_tmp,4096,0x000000);
    posix_memalign((void *)&c2h_align_mem_tmp,4096,0x000000);

    if(NULL == h2c_align_mem_tmp || NULL == c2h_align_mem_tmp)
        return -6;

    return 1;
}
void pcie_deinit()
{
    close(c2h_dma_fd);
    close(h2c_dma_fd);
}
unsigned int h2c_transfer(unsigned int size)
{
    double bd=0;
    long s=0;
    long ns=0;
    static struct timespec ts_start, ts_end;
    clock_gettime(CLOCK_MONOTONIC, &ts_start);
    int i = 0;
    while(i < 1024)
    {
       lseek(h2c_dma_fd,FPGA_DDR_START_ADDR,SEEK_SET);
       write(h2c_dma_fd,h2c_align_mem_tmp,size);
       i++ ;
    }
    clock_gettime(CLOCK_MONOTONIC, &ts_end);
    timespec_sub(&ts_end, &ts_start);
    s = ts_end.tv_sec;
    ns = ts_end.tv_nsec;
    bd = ((double)size)/(double)(s+(double)ns/1000000000.0)/1024.0;

    return (unsigned int)bd;
}
unsigned int c2h_transfer(unsigned int size)
{
    double bd=0;
    long s=0;
    long ns=0;
    static struct timespec ts_start, ts_end;
    clock_gettime(CLOCK_MONOTONIC, &ts_start);
    int i = 0;
    while(i < 1024)
    {
        lseek(c2h_dma_fd,FPGA_DDR_START_ADDR,SEEK_SET);
        read(c2h_dma_fd,c2h_align_mem_tmp,size);
        i++;
    }
    clock_gettime(CLOCK_MONOTONIC, &ts_end);
    timespec_sub(&ts_end, &ts_start);
    s = ts_end.tv_sec;
    ns = ts_end.tv_nsec;
    bd = ((double)size)/(double)(s+(double)ns/1000000000.0)/1024.0;

    return (unsigned int)bd;
}


#ifdef __cplusplus
}
#endif
